home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-02 / asorts.zip / ASORTS.MAN < prev    next >
Text File  |  1993-01-04  |  21KB  |  524 lines

  1.                              ASORTS
  2.  
  3.    General-purpose routines for sorting, searching and moving
  4.                   arrays of arbitrary elements.
  5.  
  6.                  Copyright 1991, by J. W. Rider
  7.  
  8.  
  9. ASORTS provides the Turbo Pascal programmer with type
  10. definitions, functions and procedures to handle a variety of
  11. array sorting and searching tasks.  Several of these are
  12. analogous to functions of the same name in the C programming
  13. language:
  14.  
  15.     qsort   --  sort an array
  16.  
  17.     bsearch --  do a binary search of a sorted array for an
  18.                 element that satisfies some key
  19.  
  20.     lfind   --  do a sequential ("linear") search of an unsorted
  21.                 (or sorted) array for an element that satisfies
  22.                 some key
  23.  
  24.     lsearch --  do a sequential search of an unsorted (or sorted)
  25.                 array for an element that satisfies some key.  If
  26.                 the element is not found, then it is appended to
  27.                 the end of the array.
  28.  
  29.     (Non-C programmers: please note that "bsearch" and "lfind" -- not
  30.     "lsearch"! -- do the equivalent task for sorted and unsorted arrays,
  31.     respectively.  It would make more sense to have "bfind" to search
  32.     through a sorted array, and make "bsearch" insert a missing element
  33.     into the array at the right location. However, these routines are
  34.     provided for compatibility in converting C programs.)
  35.  
  36.     swab     -- move one portion of memory to another, swapping
  37.                 high and low order bytes in the process
  38.  
  39. There are some routines that are provided for utility above and
  40. beyond what is available in standard C libraries:
  41.  
  42.     bfind    -- do a binary search of a sorted array for an
  43.                 element that satisfies some key. (This differs
  44.                 from "bsearch" in that is "bfind" reports the
  45.                 "insertion index" if the key element is not
  46.                 found.)
  47.  
  48.     binsert  -- do a binary search of a sorted array for an
  49.                 element that satisfies some key.  If the element
  50.                 is not found, then the key element is inserted in
  51.                 the proper location in the array to maintain the
  52.                 sorted order.
  53.  
  54.     fibsearch-- searches an ordered array using a "fibonacci" search
  55.                 algorithm.
  56.  
  57.     fill     -- moves a single item into all of the elements of
  58.                 an array
  59.  
  60.     heapsort -- order an array by the "heapsort" algorithm.
  61.  
  62.     longaddr -- returns the address of a variable expressed as a
  63.                 longint value
  64.  
  65.     naivesort-- a particularly inefficient sorting algorithm that the
  66.                 author has been known to use when "qsort" was not
  67.                 available. (The use of "naivesort" in applications is
  68.                 NOT recommended!)
  69.  
  70.     scan     -- returns the index of the first element in an array
  71.                 that meets a specific criterion.
  72.  
  73.     selsort  -- "selection" sorting, another sorting routine.
  74.  
  75.     shellsort-- yet another sorting routine, different from the rest.
  76.  
  77.     shuffle  -- randomly permutes ("unsorts") the elements of an
  78.                 array
  79.  
  80.     subfill  -- moves a single item into all of the elements of a
  81.                 subarray of the base array
  82.  
  83.     submove  -- moves the elements of the source array into a
  84.                 subarray of the destination array (or, the
  85.                 elements of a subarray of the source to the
  86.                 destination array)
  87.  
  88.     swap     -- Exchanges two array elements or variables.
  89.  
  90.                 (NOTE!  The "Asorts.Swap" procedure replaces the
  91.                 default "System.Swap" which simply exchanges the
  92.                 high and low-order bytes of a two byte expression.)
  93.  
  94.     vqsort   -- a quicksort algorithm for "virtual" arrays.  "VQSort"
  95.                 does not presume any kind of structure inherent within
  96.                 the "thing" being sorted. Instead, "VQSort" provides
  97.                 the sorting logic to other routines that will perform
  98.                 the actual comparisons and exchanges.
  99.  
  100.     vselsort -- a "virtual" selection sort.
  101.  
  102.     vshuffle -- a "virtual" shuffler.
  103.  
  104.     xsubmove -- moves the elements of a subarray of the source
  105.                 array into the elements of a subarray of the
  106.                 destination.
  107.  
  108.  
  109. While these routines are provided to be of assistance to the programmer,
  110. the number of different searching and sorting algorithms does raise the
  111. issue of how to select any one algorithm for the programmer to employ.
  112. Unfortunately, that is very application dependent.  For general purpose
  113. array sorting, you would do well to compare the "qsort" and "shellsort"
  114. routines on your actual data.  For sorting of typed files, I'd suggest a
  115. variant of "VSelSort".
  116.  
  117.  
  118. CONCEPTS
  119.  
  120. The ARRAYs to be manipulated are passed as "untyped vars" to
  121. these routines.  (In the "Interface" section, these arrays are
  122. called "base", "source" or "destination".)  These routines will
  123. treat the ARRAYs as if they were declared to be of type:
  124.  
  125.          ArrayType = ARRAY [1..MaxArray] OF ElementType
  126.  
  127.    WARNING!  It is *very* important to avoid defining an array
  128.    so that the last byte is in a memory segment different from
  129.    the first byte.  As long as you never declare an array larger
  130.    than 65519, or $10000-15, bytes, it should not be a problem.
  131.  
  132. Each ELEMENT of the ARRAY is presumed to be fixed size, and this
  133. size must be passed to the routines.  (In the "Interface"
  134. section, if an ELEMENT needs to passed directly to a routine as
  135. an argument, it is passed as an untyped var called "key".) Also,
  136. the number of elements in the ARRAY must also be passed.  For
  137. instance, to fill an array of real numbers with 0:
  138.  
  139.     var RealArray : array [1..10] of Real;
  140.         x         : real;
  141.  
  142.     x:=0;
  143.     fill(RealArray,10,sizeof(real),x);
  144.  
  145. A SUBARRAY is a "byte-slice" of an array.  For instance, if
  146. "ElementType" is an "array [1..8] of byte", then a "subelement"
  147. would be any contiguous collection of bytes within the
  148. element, like 3,4 and 5.  The SUBARRAY would be the collection of
  149. all of the subelements stored in an ARRAY.  If "ElementType" is a
  150. record of fields, then a "subelement" would be any contigous
  151. group of fields.
  152.  
  153. For sorting and searching, a COMPARISON FUNCTION must be passed
  154. to the routines.  COMPARISON FUNCTIONs take two untyped vars,
  155. return a longint value, and must be declared "far" at
  156. compilation. (DIFFERENT! In C, only an integer-sized value is
  157. returned.)  For instance, to sort the array of real numbers
  158. declared earlier:
  159.  
  160.     function RealCompare(var a,b):longint; far;
  161.     begin
  162.        if real(a)>real(b) then realcompare:=1
  163.        else if real(a)<real(b) then realcompare:=-1
  164.        else realcompare:=0;
  165.     end;
  166.  
  167.     qsort(realarray,10,sizeof(real),realcompare);
  168.  
  169.  
  170. "Virtual" arrays are data structures whose elements can be accessed
  171. indirectly by an index.  For instance, information that is physically
  172. stored in multiple arrays might be sorted by a key in just one of the
  173. arrays.  ASORTS provides a few routines for handling such "virtual"
  174. arrays.  "VQSort" and "VSelSort" will provide the sorting logic for
  175. ordering the arrays.  "VShuffle" will similarly "unorder" the array.
  176. To sort an array of "DBRec" with respect to an array of integer
  177. priorities, declare:
  178.  
  179.    var Array1 : array [1..MaxDBRec] of DBRec;
  180.        Priority: array [1..MaxDBRec] of integer;
  181.  
  182.    function ComparePriority(a,b:longint):longint; far;
  183.    begin ComparePriority:=longint(Priority[a])-Priority[b] end;
  184.  
  185.    procedure SwapPriDBRec(a,b:longint); far;
  186.    begin asorts.swap(Priority[a],Priority[b],sizeof(integer));
  187.          asorts.swap(Array1[a],Array1[b],sizeof(DBRec)); end;
  188.  
  189. and sort the two arrays with:
  190.  
  191.    vqsort(MaxDBRec,ComparePriority,SwapPriDBRec);
  192.  
  193.  
  194. INTERFACE
  195.  
  196. function bfind(var key{:element}, base{:array};
  197.                    length_base, sizeof_element:word;
  198.                    f:comparefunc):longint;
  199.  
  200.     Searches a sorted array for a "key" element. Return the index
  201.     of its location, or the negative of the index of the largest
  202.     element in the array that is smaller than the key (i.e., the
  203.     element that you want to insert the new element after).
  204.  
  205.  
  206. function binsert(var key{:element}, base{:array};
  207.                      length_base, sizeof_element:word;
  208.                      f:comparefunc):longint;
  209.  
  210.    WARNING: This routine overwrites memory if used incorrectly.
  211.  
  212.     Inserts the key element into the correct position of an
  213.     ordered array. Unlike "lsearch", which only adds the key if
  214.     it's not already present, "binsert" ALWAYS inserts a new
  215.     element into the array. "Binsert" returns the index where the
  216.     element is inserted.
  217.  
  218.  
  219. function bsearch(var key{:element}, base{:array};
  220.                      length_base, sizeof_element:word;
  221.                      f:comparefunc):word;
  222.  
  223.     "Bsearch" attempts to locate the "key" element within the
  224.     previously SORTED array "base".  If successful, the index
  225.     of the found element is returned; otherwise, 0 is returned
  226.     to indicate that the element is not present.
  227.  
  228.  
  229. type comparefunc = function (var a,b{:element}):longint; {far;}
  230.  
  231.     Declares the type of the comparison function to be passed to
  232.     sorting and searching routines. CompareFunc's are
  233.     user-defined functions that takes two arguments a and b. A
  234.     and B must be typeless in the declaration, but otherwise are
  235.     of the same type as the elements of the "base" array. For
  236.     "qsort" and "bsearch", the function needs to return a
  237.     negative integer if "A<B"; a positive integer if "A>B"; and 0
  238.     if "A=B". For "lfind" and "lsearch", the function needs to
  239.     return 0 if "A=B", and some non-zero integer if "A<>B".
  240.  
  241.  
  242. function fibsearch(var key{:element}, base{:array};
  243.                        length_base, sizeof_element:word;
  244.                        f:comparefunc):word;
  245.  
  246.     "Fibsearch" attempts to locate the "key" element within the
  247.     previously SORTED array "base".  If successful, the index
  248.     of the found element is returned; otherwise, 0 is returned
  249.     to indicate that the element is not present.  (This procedure
  250.     is included because a user asked about the algorithm on one
  251.     of Borland's CompuServe Forums.  It looks like an interesting
  252.     alternative to "bsearch", but I have not run extensive
  253.     comparisons.)
  254.  
  255.  
  256. procedure fill(var key{:element}, destination{:array};
  257.                    count, sizeof_element:word);
  258.  
  259.    WARNING: This routine overwrites memory if used incorrectly.
  260.  
  261.     Moves the "key" element to the first "count" indices in the
  262.     "destination" array.
  263.  
  264.  
  265. procedure heapsort(var base {:array};
  266.                    length_base, sizeof_element:word;
  267.                    f:comparefunc);
  268.  
  269.    WARNING: This routine overwrites memory if used incorrectly.
  270.  
  271.     "HeapSort" reorders the elements of the "base" array using a
  272.     heapsort algorithm (like, you expected something else?).  The
  273.     function "f" is used to compare two elements of the array, and
  274.     must return a negative number if the first argument is "less
  275.     than" the second, a postive number if the first argument is
  276.     "greater than" the second, and zero if the two arguments are
  277.     "equal".
  278.  
  279.  
  280. type icomparefunc = function (a,b:longint):longint;
  281.  
  282.     Declares the type of the comparison function to be passed as
  283.     an argument for sorting "virtual" arrays.  Instead of passing
  284.     the elements to be compared as is done for "comparefunc",
  285.     ICompareFunc's use the location of the elements.
  286.  
  287.  
  288. function lfind(var key{:element}, base{:array};
  289.                    length_base, sizeof_element:word;
  290.                    f:comparefunc):word;
  291.  
  292.     "Lfind" attempts to locate the "key" element within the array
  293.     "base".  If successful, the index of the found element is
  294.     returned; otherwise, 0 is returnd to indicate the element is
  295.     not present.
  296.  
  297.  
  298. function longaddr (var x): longint;
  299.  
  300.     Returns the address of a variable expressed as a longint value
  301.     so that address can be used for comparisons, etc.
  302.  
  303.  
  304. function lsearch(var key{:element}, base{:array};
  305.                      length_base, sizeof_element:word;
  306.                      f:comparefunc):word;
  307.  
  308.    WARNING: This routine overwrites memory if used incorrectly.
  309.  
  310.  
  311.     Does a linear search of the "base" array, looking for the "key"
  312.     element.  If the key element is found, "lsearch" returns the
  313.     index of the array.  *** NOTE! ***  Otherwise, the key element
  314.     is appended to the end of the array.  It is the programmer's
  315.     responsibility to ensure that "sizeof_element" bytes are
  316.     available at the end of the array and that the count of
  317.     contained elements is adjusted.  To avoid the append, use
  318.     "lfind" instead.
  319.  
  320.  
  321. var monitor : procedure;
  322. procedure nullmonitor;
  323.  
  324.     "Monitor" and "NullMonitor" were debugging devices developed
  325.     in the process of putting together the ASORTS unit. They can
  326.     be optionally declared by defining a compilation variable
  327.     "MONITOR". Every time that the "Qsort" algorithm swaps a pair
  328.     of array elements, the "monitor" procedure is called. This
  329.     will allow the user to watch the progress of the sort. This
  330.     is of marginal practical value, and by default, these two
  331.     identifiers are not defined. Calling "NullMonitor" will turn
  332.     off monitoring even if the "monitor" procedure variable is
  333.     defined.
  334.  
  335.  
  336. procedure naivesort(var base {:array};
  337.                     length_base, sizeof_element:word;
  338.                     f:comparefunc);
  339.  
  340.   WARNING: This routine slowly overwrites memory if used incorrectly.
  341.  
  342.     "NaiveSort" also reorders the elements or an array.  However,
  343.     it does it more slowly than "QSort".  The use of the procedure
  344.     is not recommended.  It is provided for comparison only.  (i.e.,
  345.     don't waste your time trying to improve upon it.  It's only here
  346.     for comic relief.)
  347.  
  348.  
  349. procedure qsort(var base {:array};
  350.                     length_base, sizeof_element:word;
  351.                     f:comparefunc);
  352.  
  353.    WARNING: This routine overwrites memory if used incorrectly.
  354.  
  355.     "Qsort" reorders the elements of the "base" array using a
  356.     quicksort algorithm.  The function "f" is used to compare
  357.     two elements of the array, and must return a negative
  358.     number if the first argument is "less than" the second, a
  359.     postive number if the first argument is "greater than" the
  360.     second, and zero if the two arguments are "equal".
  361.  
  362.  
  363. function scan(var source; count, sizeof_element:word;
  364.                   f:testfunc):word;
  365.  
  366.     "Scan" does a linear search of the "source" array and
  367.     returns the index of the first element that satisfies the
  368.     test function "f".  If no element is found, "scan" returns 0.
  369.  
  370.  
  371. procedure selsort(var base{:array};
  372.                   length_base, sizeof_element:word;
  373.                   f:comparefunc);
  374.  
  375.    WARNING: This routine overwrites memory if used incorrectly.
  376.  
  377.     "SelSort" reorders the elements of the "base" array using a
  378.     selection sort algorithm.  This algorithm minimizes the number
  379.     of times that each element is moved, but will maximize the
  380.     of comparisons.  For most applications, the comparisons take
  381.     more time than the exchanges, so you are not likely to want to
  382.     use this algorithm for ordinary array sorting.  See also,
  383.     "VSelSort".
  384.  
  385.  
  386. procedure shellsort(var base{:array};
  387.                     length_base, sizeof_element:word;
  388.                     f:comparefunc);
  389.  
  390.    WARNING: This routine overwrites memory if used incorrectly.
  391.  
  392.     "ShellSort" reorders the elements of the "base" array using a
  393.     sort routine first defined by an individual whose last name was
  394.     Shell.  In concept, the algorithm is an insertion-type sort (as
  395.     opposed to exchange-type sort (of which "qsort" and "selsort"
  396.     are examples).  This turns out to be a very efficient sorting
  397.     routine for in-memory sorting.
  398.  
  399.  
  400. procedure shuffle(var base{:array};
  401.                       length_base, sizeof_element:word);
  402.  
  403.    WARNING: This routine overwrites memory if used incorrectly.
  404.  
  405.     Randomly permutes ("unsorts") the elements of an array. The
  406.     SYSTEM "Randomize" procedure should be called at least once
  407.     in a program that shuffles an array.
  408.  
  409.  
  410. procedure subfill(var key{:subelement}, destination{:subarray};
  411.                   count, sizeof_key, sizeof_element:word);
  412.  
  413.    WARNING: This routine overwrites memory if used incorrectly.
  414.  
  415.     Partially fills an array with the "key". The address of
  416.     "Destination" should be the address of the first byte in the
  417.     subarray. The portion of the array outside of the subarray is
  418.     left unchanged.
  419.  
  420.  
  421. procedure submove(var source{:[sub]array},
  422.                       destination{:[sub]array};
  423.                       count,
  424.                       sizeof_source_element,
  425.                       sizeof_destination_element:word);
  426.  
  427.    WARNING: This routine overwrites memory if used incorrectly.
  428.  
  429.     If the size of the source elements are smaller than that of
  430.     the destination elements, moves the first "count" elements of
  431.     the source into a subarray of the same size in destination.
  432.     If larger, only moves that portion of the source that will
  433.     fill the first "count" elements of the destination. If equal
  434.     in size, does a simple "move" of the bytes.
  435.  
  436.  
  437. procedure swab(var source, destination; numwords:word);
  438.  
  439.    WARNING: This routine overwrites memory if used incorrectly.
  440.  
  441.     Moves the contents of source to the destination, swapping
  442.     high and low order bytes in the process.  (Note:  while this
  443.     is provided for C compatibility, the third argument is used
  444.     differently.  In C, the third argument is the number of bytes
  445.     to move and is expected to be even.  Here, the third argument
  446.     is the number of byte-pairs to move.)
  447.  
  448.  
  449. procedure swap(var var1,var2; sizeof_element:word);
  450.  
  451.    WARNING: This routine overwrites memory if used incorrectly.
  452.  
  453.     "Swap" exchanges the contents of the variable "var1" with
  454.     the contents of the variable "var2".  (Note: this is a
  455.     redefinition of the "swap" function defined in the System
  456.     unit.)
  457.  
  458.  
  459. type swapproc = procedure (a,b:longint); {far;}
  460.  
  461.     Declares the type of the exchange procedure that is passed as an
  462.     argument to "selsort" and other virtual exchange sort algorithms.
  463.  
  464.  
  465. type testfunc = function (var a):boolean; {far;}
  466.  
  467.     Declares the type of the criterion function to be passed to
  468.     the "scan" function.  The actual TestFunc should expect an
  469.     array element to be passed through "a" and return true if the
  470.     element satisfies the criteria.  Otherwise, it should return
  471.     false.
  472.  
  473.  
  474. procedure vqsort(length_base:longint; f:icomparefunc; s:swapproc);
  475.  
  476.     "VQSort" provides the logic to do a quicksort of an indexed
  477.     entity (a "virtual" array), but depends upon the user-defined
  478.     routines "f" and "s" to do the actual work of accessing specific
  479.     elements in the array, comparing and exchanging them.  This
  480.     makes VQSort useful for sorting elements when they are stored
  481.     in something other than a single contiguous array.
  482.  
  483.  
  484. procedure vselsort(length_base:longint; f:icomparefunc; s:swapproc);
  485.  
  486.     "VSelSort" provides the logic to do a selection sort of an
  487.     indexed entity (a "virtual" array), but depends upon the
  488.     user-defined routines "f" and "s" to do the actual work of
  489.     accessing specific elements in the array, comparing and
  490.     exchanging them.  As mentioned in the description for "SelSort",
  491.     the algorithm minimizes the number of exchanges of elements
  492.     required to put something into sorted order, but makes a large
  493.     number of comparisons to do so.  This would make "VSelSort"
  494.     useful for something like sorting an external file of larger
  495.     records based upon integer keys stored in an array in memory.
  496.  
  497.  
  498. procedure vshuffle(length_base:longint; s:swapproc);
  499.  
  500.     Of course, what fun is there in being able to order virtual
  501.     arrays if we can't mix all the elements up again?
  502.  
  503. procedure xsubmove(var source{:subarray}, destination{:subarray};
  504.                        count, sizeof_source_element,
  505.                        sizeof_destination_element,
  506.                        sizeof_subelement:word);
  507.  
  508.    WARNING: This routine overwrites memory if used incorrectly.
  509.  
  510.     Moves a subarray from the source to the destination. The size
  511.     of the subelements is presumed to be the same in both
  512.     subarrays. "XsubMove" does not check to make sure that the
  513.     sizes are consistent.
  514.  
  515.  
  516. REFERENCES
  517.  
  518. Knuth, The Art of Computer Programming, Sorting and Searching.
  519.  
  520. Press, et al., Numerical Recipes.
  521.  
  522. Sedgewick, Algorithms.
  523.  
  524.